#####################################################################
# File:         Messaging.py
# Product:      KnowledgeEngine
#
# Copyright:    Copyright (c) 2002, Faulkner Ltd.
# Author:       Brian Oliver
#
# Description:  This file defines the Messaging class that defines
#               a publish/subscribe messaging system for the 
#               Knowledge Engine.
#
#               More specifically, the Messaging class permits the
#               registration of instances that implement the 
#               MessageListener interface/mix-in, responsible
#               for handling "Messages" published by the Messaging
#               system on behalf of Message publishers.
#
#               Additionally the Messaging class permits the definition
#               of arbitary message filters, topcs and groups to simplify
#               the registration and processing of a variety of messages.
#
# Concepts:     This class introduces the following concepts; Topics, 
#               Messages, MessageListeners, Messaging System, 
#               Publishers, Subscribers, Groups and Filters.
#
#               A "Publisher" is an object/module that produces 
#               "Messages" with arbitary "Topics" to be distributed 
#               to "Subscribers" by a "Messaging System".
#
#               A "Subscriber" is an object that implements the 
#               "MessageListener" interface to handle/consume "Messages" 
#               broadcast by "Messaging Systems".  Subscribers must be registered
#               with "Messaging Systems" for named "Topics", "Filters" or 
#               "Groups" in order to receive messages broadcast on behalf
#               of "Publushers"
#
#               A "Message" is an object encapsulating necessary information
#               (parameters/payload) for a system event.  Each "Message" has
#               a defined "Topic" that relates to the type/purpose of 
#               the "Message". Typically the payload of a "Message" is 
#               dependant on type of "Message" being broadcast.
#
#               A "Topic" is a name that specifies the type/purpose 
#               of "Message".  Topics are used by "Publishers", "Subscribers" 
#               and "Messaging Systems" to route, "Filter" and "Group" "Messages".
#
#               The "MessageListener" interface defines a simple method called
#               'onMessage(message)' to handle the delivery and processing of
#               a specified message from a "Messaging System".
#
#               A "Messaging System" provides functionality to broadcast "Messages"
#               to "Subscribers" of particular "Topics" on behalf of "Publishers".  
#               "Messaging Systems" typically also permit the definition of 
#               named msssage "Filters" and named "Groups" of related "Topics" to 
#               which "Subscribers" may also subscribe.  This class represents a
#               simple implementation of a "Messaging System".
#
#               A "Group" is a named set of "Topics/Filters" to which a 
#               "Subscriber" may subscribe.
#
#               A "Filter" is a named function that may be used by a "Messaging System"
#               to filter messages.  Like "Groups", "Subscribers" may also subscribe to 
#               named "Filters".
#
# Note:         Topic, Filter and Group names are case-sensitive
#
# See Also:     Message, MessageListener
#####################################################################

#####################################################################
## KnowledgeEngine Library Imports
from TopicListeners import *
from FilterListeners import *
from GroupListeners import *
from Message import *

#####################################################################
## Zope Library Imports
# (none)
 
#####################################################################
## Python Library Imports
# (none)

#####################################################################
## MessagingSystem Class Definition
class Messaging:
    """
        A class to that provides Messaging System services for
        Message publishers and subscribers.
    """
        
    #---------------------------------------------------------------#
            
    #the filters, topics and groups
    filters = {}       #{filterName:FilterListeners}
    groups = {}        #{groupName:GroupListeners}
    topics = {}        #{topicName:TopicListeners}
    
    #the owner object of the messaging system
    owner = None
    
    #---------------------------------------------------------------#

    def __init__(self, owner):
        """
            Constructor for the Messaging System, with the 
            specified owner (a GenericObject)
        """
        
        self.filters = {}       #{filterName:FilterListeners}
        self.groups = {}        #{groupName:({names},[MessageListener])}
        self.topics = {}        #{topicName:TopicListeners}
                
        self.owner = owner
                
    #---------------------------------------------------------------#

    def getOwner(self):
        """
            Returns the owner (GenericObject) of the messaging system
        """
        
        return self.owner
                
    #---------------------------------------------------------------#

    def setModified(self):
        """
            Sets that the messaging system has had its internal
            state modified
        """
        
        self.getOwner().setModified()
                
    #---------------------------------------------------------------#

    def subscribeFor(self, name, messageListener):
        """
            Attempt to subscribe the specified listener for the
            specified named filter, group or topic.  
            
            This method first attempts to subscribe the listener
            for a defined filter with the specified name.  If the
            named filter does not exist, an attempt it made to 
            subscribe the listener for a defined group with the
            specified name. Should no filter or group exist, the
            listener is subscribed to a topic with the specified
            name.
        """

        #does a filter exist?
        if self.filters.has_key(name):
            filter = self.filters[name]
            filter.addListener(messageListener)            
            self.setModified()
        
        #does a group exist?
        elif self.groups.has_key(name):
            group = self.groups[name]
            group.addListener(messageListener)
            self.setModified()
        
        #subscribe to the topic
        else:
            #does the topic exist?
            if self.topics.has_key(name):
                topic = self.topics[name]
            
            #topic doesn't exist
            else:
                topic = TopicListeners(name)
                self.topics[name] = topic
        
            #add the listener to the topic                
            topic.addListener(messageListener)
            self.setModified()
        
    #---------------------------------------------------------------#

    def unsubscribeFor(self, name, messageListener):
        """
            Unsubscribes the specified listener from the named
            filter/group/topics
        """
        
        #does the filter exist?
        if self.filters.has_key(name):
            filters[name].removeListener(messageListener)
            self.setModified()
        
        #does the group exist?
        if self.groups.has_key(name):
            groups[name].removeListener(messageListener)
            self.setModified()
        
        #does the topic exist?
        if self.topics.has_key(name):
            topics[name].removeListener(messageListener)        
            self.setModified()
        
    #---------------------------------------------------------------#

    def publishMessage(self, message):
        """
            Broadcasts the specified message to all of the 
            appropriately registered subscribers.
        """
        
        ###print "DEBUG: Messaging.publishMessage(%s) publishing" % (message.getTopicName(),)

        #process topics
        for topic in self.topics.values():
        
            #is the message topic the same as the topic name?
            if topic.getName() == message.getTopicName():
            
                # broadcast message to all of the topic listeners
                self._broadcast(message, topic.getListeners())
                
                #broadcast message to all of the groups that contain this topic
                for group in self.groups.values():
                    if group.containsName(topic.getName()):
                        self._broadcast(message, group.getListeners())
                
        #process filters
        for filter in self.filters.values():
            
            #apply the message to the filter 
            if filter.getFunction()(message):
                self._broadcast(message, filter.getListeners())

                #broadcast message to all of the groups that contain this filter
                for group in self.groups.values():
                    if group.containsName(filter.getName()):
                        self._broadcast(message, group.getListeners())                

    #---------------------------------------------------------------#

    def _broadcast(self, message, listeners):
        """
            Broadcasts the specified message to the list of listeners
            while the message has not been consumed.
        """
        
        #has the message been consumed already?
        if not message.hasBeenConsumed():

            #send the message to each of the listeners
            for listener in listeners:

                #send the message
                listener.onMessage(message)
                
                #has the message been consumed?
                if message.hasBeenConsumed():
                    #force terminate!
                    return
        
    #---------------------------------------------------------------#

    def createGroup(self, name, names = []):
        """
            Create a group with the specified name containing the
            specified list of topic/filter names.
        """

        #is the group already defined?
        if not self.groups.has_key(name):
            group = GroupListeners(name, names)
            self.groups[name] = group
            self.setModified()

            ###print "DEBUG: Messaging.createGroup(%s, %s) Created for Environment '%s'" % (name, repr(names), self.getOwner().getTitle())
        
    #---------------------------------------------------------------#

    def _createFilter(self, name, filterFunction):
        """
            Create a filter with the specified name using the
            specified message filter function
        """

        #is the filter already defined?
        if not self.filters.has_key(name):
            filter = FilterListeners(name, filterFunction)
            self.filters[name] = filter
            self.setModified()
            
            ###print "DEBUG: Messaging.createFilter(%s) Created for Environment '%s'" % (name, self.getOwner().getTitle())
            
    #---------------------------------------------------------------#

#####################################################################
## Class Initialisation
# (none)
